#include "objectmanager.h"
#include <Shinygami/datatree.hpp>
#include "yamlstream.h"
#include <QFile>
#include <QTextStream>
#include <iostream>

ObjectManager::ObjectManager(QObject *parent) :
    QObject(parent)
{
}

void ObjectManager::AddObject(Object object)
{
    _objects << object;
    Updated();
}

void ObjectManager::DelObject(const QString &name)
{
    QList<Object>::iterator current = _objects.begin();
    QList<Object>::iterator end     = _objects.end();

    for (; current != end ; ++current)
    {
        if ((*current).name == name)
        {
            _objects.erase(current);
            break ;
        }
    }
}

void ObjectManager::SaveObject(const Object object)
{
    QList<Object>::iterator current = _objects.begin();
    QList<Object>::iterator end     = _objects.end();

    for (; current != end ; ++current)
    {
        if (current->name == object.name)
        {
            _objects.erase(current);
            _objects << object;
            break ;
        }
    }
}

ObjectManager::Object* ObjectManager::GetObject(const QString &name)
{
    QList<Object>::iterator current = _objects.begin();
    QList<Object>::iterator end     = _objects.end();

    while (current != end)
    {
        if (current->name == name)
            return (&(*current));
        ++current;
    }
    return (0);
}

void ObjectManager::Load(const QString& filepath)
{
  DataTree* file = DataTree::Factory::Yaml(filepath.toStdString());
  Data      data(file);

  if (file)
  {
    Data::iterator current = data.begin();
    Data::iterator end     = data.end();

    for (; current != end ; ++current)
    {
      Object                       object;
      Data::iterator objCurrent = (*current).begin();
      Data::iterator objEnd     = (*current).end();

      object.name = (*current).Key().c_str();
      for (; objCurrent != objEnd ; ++objCurrent)
      {
        if      ((*objCurrent).Key() == "type")
          object.type             = (*objCurrent).Value().c_str();
        else if ((*objCurrent).Key() == "bullet")
          object.isBullet         = (*objCurrent) == "1";
        else if ((*objCurrent).Key() == "fixedRotation")
          object.hasFixedRotation = (*objCurrent) == "1";
        else if ((*objCurrent).Key() == "shape")
        {
          Data fieldShape     = (*objCurrent);
          Shape       shape;

          shape.name     = fieldShape["name"].Value().c_str();
          shape.type     = fieldShape["type"].Value().c_str();
          shape.friction = fieldShape["friction"];
          shape.density  = fieldShape["density"];
          shape.sensor   = fieldShape["sensor"] == "1";

          Data collisionGroup = fieldShape["collisionGroup"];
          if (!collisionGroup.Nil())
            shape.collisionGroup = collisionGroup.Value().c_str();

          if      (shape.type == "box")
          {
            shape.size.x     = fieldShape["size"]["x"];
            shape.size.y     = fieldShape["size"]["y"];
            shape.position.x = fieldShape["position"]["x"];
            shape.position.y = fieldShape["position"]["y"];
            shape.angle      = fieldShape["angle"];
          }
          else if (shape.type == "circle")
          {
            shape.size.x     = fieldShape["radius"];
            shape.position.x = fieldShape["position"]["x"];
            shape.position.y = fieldShape["position"]["y"];
          }
          else if (shape.type == "polygon")
          {
            Data::iterator verticesIt  = fieldShape["vertices"].begin();
            Data::iterator verticesEnd = fieldShape["vertices"].end();

            for (; verticesIt != verticesEnd ; ++verticesIt)
            {
              Data         fieldPoint = (*verticesIt);
              sf::Vector2f point;

              point.x = fieldPoint["x"];
              point.y = fieldPoint["y"];
              shape.vertices << point;
            }
          }
          object.fixtures << shape;
        }
        else if ((*objCurrent).Key() == "graphics")
        {
          Data::iterator currentGraphics = (*objCurrent).begin();
          Data::iterator endGraphics     = (*objCurrent).end();

          for (; currentGraphics != endGraphics ; ++currentGraphics)
          {
            if ((*currentGraphics).Key() == "texture")
              object.textures   << (*currentGraphics).Value().c_str();
            if ((*currentGraphics).Key() == "animation")
              object.animations << (*currentGraphics).Value().c_str();
          }
        }
      }
      AddObject(object);
    }
  }
}

/*
 * SAVING CODE
 */
ObjectManager::funcSaveShape ObjectManager::_funcsShapeSave[] = {
    { "box",     &ObjectManager::SaveShapeBox     },
    { "circle",  &ObjectManager::SaveShapeCircle  },
    { "polygon", &ObjectManager::SaveShapePolygon },
    { "", 0 }
};

void ObjectManager::SaveGraphics(YamlStream& stream, const QString& prefix, const QList<QString>& graphics)
{
    foreach (QString name, graphics)
    {
        QPair<QString, std::string> tex;

        tex.first  = prefix;
        tex.second = name.toStdString();
        stream << tex;
    }
}

void ObjectManager::SaveShapeBox(YamlStream &stream, const Shape& shape)
{
    QPair<QString, float> sizeX("x", shape.size.x);
    QPair<QString, float> sizeY("y", shape.size.y);
    QPair<QString, float> posX("x", shape.position.x);
    QPair<QString, float> posY("y", shape.position.y);
    QPair<QString, float> angle("angle", shape.angle);

    stream << angle;
    stream.newBlock("size");
    stream << sizeX << sizeY;
    stream.endBlock();
    stream.newBlock("position");
    stream << posX << posY;
    stream.endBlock();
}

void ObjectManager::SaveShapeCircle(YamlStream& stream, const Shape& shape)
{
    QPair<QString, float> posX("x", shape.position.x);
    QPair<QString, float> posY("y", shape.position.y);

    stream << QPair<QString, float>("radius", shape.size.x);
    stream.newBlock("position");
    stream << posX << posY;
    stream.endBlock();
}

void ObjectManager::SaveShapePolygon(YamlStream& stream, const Shape& shape)
{
    stream.newBlock("vertices");
    foreach (sf::Vector2f point, shape.vertices)
    {
        QPair<QString, float> x("x", point.x);
        QPair<QString, float> y("y", point.y);

        stream.newBlock("point");
        stream << x << y;
        stream.endBlock();
    }

    stream.endBlock();
}

void ObjectManager::Save(const QString& filepath)
{
    QFile      file(filepath);
    YamlStream stream;

    foreach (Object object, _objects)
    {
        QPair<QString, std::string> type("type", object.type.toStdString());
        QPair<QString, bool>        bullet("bullet", object.isBullet);
        QPair<QString, bool>        fixedRotation("fixedRotation", object.hasFixedRotation);

        stream.newBlock(object.name);
        stream << type << bullet << fixedRotation;
        if (object.textures.count() > 0 || object.animations.count() > 0)
        {
          stream.newBlock("graphics");
          SaveGraphics(stream, "texture",   object.textures);
          SaveGraphics(stream, "animation", object.animations);
          stream.endBlock();
        }

        foreach (ObjectManager::Shape shape, object.fixtures)
        {
          QPair<QString, std::string> shapeType("type",    shape.type.toStdString());
          QPair<QString, std::string> shapeName("name",    shape.name.toStdString());
          QPair<QString, float>       density("density",   shape.density);
          QPair<QString, float>       friction("friction", shape.friction);
          QPair<QString, bool>        sensor("sensor",     shape.sensor);
	  QPair<QString, std::string> collisionGroup("collisionGroup",    shape.collisionGroup.toStdString());

          stream.newBlock("shape");
          stream << shapeName << shapeType << density << friction;
          if (shape.sensor)
            stream << sensor;
	  if (shape.collisionGroup != "")
	    stream << collisionGroup;
          for (unsigned char i = 0 ; _funcsShapeSave[i].function != 0 ; ++i)
          {
            if (_funcsShapeSave[i].key == shape.type)
            {
              _funcsShapeSave[i].function(stream, shape);
              break ;
            }
          }
          stream.endBlock();
        }
        stream.endBlock();
    }

    if (file.open(QFile::WriteOnly))
    {
        QTextStream out(&file);

        out << stream.str().c_str();
        file.close();
    }
}
